package com.bluedot.www.core.service;


import com.bluedot.www.core.dao.Execute;
import com.bluedot.www.core.pojo.DO.Algorithm;
import com.bluedot.www.core.pojo.VO.ViewAlgorithmInfo;
import com.bluedot.www.core.utils.AlgorithmUtil;
import com.bluedot.www.core.utils.DbUtil;
import com.bluedot.www.core.utils.OSSManageUtil;
import com.bluedot.www.framework.mvc.servlet.http.MultipartFile;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * <p>
 * 算法表，包括预处理算法和分析算法 服务类
 * </p>
 *
 * @author zhouxuan
 * @since 2021-08-07
 */
public class AlgorithmService {

    private static final String ALGORITHM_MAPPER_ADDRESS = "com.bluedot.www.core.mapper.xml.AlgorithmMapper.";
    /**
     * 存储所有的算法Class对象
     */
    private static final Map<String, Class<?>> algorithmClass = new HashMap<>(8);
    /**
     * 默认预处理算法方法名
     */
    private static String PRE_METHOD_NAME = "preHandleImage";
    /**
     * 默认分析算法方法名
     */
    private static String HANDLER_METHOD_NAME = "analyzeHandleImage";

    public AlgorithmService() {
        String content = "package com.bluedot.www.baiyetest.servlet.baiyetest;" +
                "class Test {\n" +
                "  public static void main(String[] args) {\n" +
                "    System.out.println(\"success\");\n" +
                "  }\n" +
                "}\n";
        String fullName = "com.bluedot.www.baiyetest.servlet.baiyetest.Test";
        try {
            Class<?> algorithmClass = AlgorithmUtil.getAlgorithmClass(fullName, content);
            AlgorithmService.algorithmClass.put(algorithmClass.getSimpleName(), algorithmClass);

        } catch (ClassNotFoundException | IllegalAccessException |
                InstantiationException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    public static Object execute(String methodName, Object[] param) {
        AlgorithmService algorithmService = new AlgorithmService();
        Object result = null;
        switch (methodName) {
            case "switchAlgorithm":
                result = algorithmService.switchAlgorithm((Long) param[0], (Long) param[1]);
                break;
            case "getDefaultAlgId":
                result = algorithmService.getDefaultAlgId();
                break;
            case "findPreAlgorithm":
                result = algorithmService.findPreAlgorithm();
                break;
            case "findAnaAlgorithm":
                result = algorithmService.findAnaAlgorithm();
                break;
            case "deleteAlgorithmByAlgorithmId":
                result = algorithmService.deleteAlgorithmByAlgorithmId((List<Long>) param[0]);
                break;
            case "uploadAlg":
                result = algorithmService.uploadAlg((HttpServletRequest) param[0]);
                break;
            case "addAlgorithm":
                result = algorithmService.addAlgorithm((Algorithm) param[0]);
                break;
            case "findAlgorithmById":
                result = algorithmService.findAlgorithmById((Long) param[0]);
                break;
            case "findAlgViewById":
                result = algorithmService.findAlgViewById((Long) param[0]);
                break;
            case "countAlgorithm":
                result = algorithmService.countAlgorithm();
                break;
            default:
                throw new RuntimeException("Method NOT FOUND, method name: " + methodName);
        }
        return result;
    }

    /**
     * 这个方法将以前使用的默认算法对应的isDefault字段更改
     *
     * @return boolean
     * @author Ding YuXing
     * @date 2021/8/31 20:05
     */
    private Integer switchOldAlgorithm() {
        Integer Result = (Integer) Execute.execute(Execute.UPDATE_TYPE, ALGORITHM_MAPPER_ADDRESS + "switchOldAlgorithm", null);
        return Result;
    }

    /**
     * 切换默认算法
     * 之前一定要有两个默认算法
     * 这个方法先将以前使用的默认算法对应的isDefault字段更改 然后将传入的算法id对应的isDefault字段改为是
     * 调两次AlgorithmMapper里面的方法switchAlgorithm将两个参数分别调一次
     *
     * @param preAlgorithmId 修改后预处理算法id
     * @param anaAlgorithmId 修改后分析算法id
     * @return boolean
     * @author Ding YuXing
     * @date 2021/8/8 10:22.
     */
    private boolean switchAlgorithm(Long preAlgorithmId, Long anaAlgorithmId) {
        Integer switchOldResult = switchOldAlgorithm();

        String preAlgId = Long.toString(preAlgorithmId);
        String anaAlgId = Long.toString(anaAlgorithmId);

        if (switchOldResult == 2) {

            Object[] objects = new Object[2];

            Map map = new HashMap<String, Object>(1);
            map.put("preAlgorithmId", preAlgId);

            Map map2 = new HashMap<String, Object>(1);
            map2.put("anaAlgorithmId", anaAlgId);

            objects[0] = map;
            objects[1] = map2;

            Integer switchResult = (Integer) Execute.execute(Execute.UPDATE_TYPE, ALGORITHM_MAPPER_ADDRESS + "switchAlgorithm", objects);
            if (switchResult == 2) {
                return true;
            } else {
                return false;
            }
        }
        return false;
    }

    /**
     * 查询数据库中默认算法的id
     *
     * @return List<Integer>
     * @author Ding YuXing
     * @date 2021/10/24 17:06
     */
    private List<Long> getDefaultAlgId() {
        List<Long> defaultAlgIdList = new ArrayList<>();
        List<Long> defaultPreAlgId;
        List<Long> defaultAnaAlgId;
        defaultPreAlgId = (List<Long>) Execute.execute(Execute.QUERY_TYPE, ALGORITHM_MAPPER_ADDRESS + "getDefaultPreAlgId", null);
        defaultAnaAlgId = (List<Long>) Execute.execute(Execute.QUERY_TYPE, ALGORITHM_MAPPER_ADDRESS + "getDefaultAnaAlgId", null);
        defaultAlgIdList.add(defaultPreAlgId.get(0));
        defaultAlgIdList.add(defaultAnaAlgId.get(0));
        return defaultAlgIdList;
    }

    /**
     * 查询数据库中所有的预处理算法
     *
     * @return List<Algorithm> 算法列表
     * @author Ding YuXing
     * @date 2021/8/8 10:23
     */
    private List<Algorithm> findPreAlgorithm() {
        List<Algorithm> preAlgorithmList;
        preAlgorithmList = (List<Algorithm>) Execute.execute(Execute.QUERY_TYPE, ALGORITHM_MAPPER_ADDRESS + "findPreAlgorithm", null);
        return preAlgorithmList;
    }

    /**
     * 查询数据库中所有的分析算法
     *
     * @return List<Algorithm> 算法列表
     * @author Ding YuXing
     * @date 2021/8/8 10:23
     */
    private List<Algorithm> findAnaAlgorithm() {
        List<Algorithm> anaAlgorithmList;
        anaAlgorithmList = (List<Algorithm>) Execute.execute(Execute.QUERY_TYPE, ALGORITHM_MAPPER_ADDRESS + "findAnaAlgorithm", null);
        return anaAlgorithmList;
    }

    /**
     * 删除算法
     *
     * @param algorithmId 算法id
     * @return boolean
     * @author Ding YuXing
     * @date 2021/8/8 10:23
     */
    private boolean deleteAlgorithmByAlgorithmId(List<Long> algorithmId) {
        //通过算法id查找算法is_default字段 如果为1则跳过删除该算法操作
        List<Long> lastAlgIds = new ArrayList<>();
        for (Long algId : algorithmId) {
            Integer isDefault = findIsDefaultByAlgId(algId);
            if (isDefault == 0) {
                lastAlgIds.add(algId);
            }
        }

        //如果要删除的全是默认算法返回false
        if (lastAlgIds.size() == 0) {
            return false;
        }

        int i = 0;
        for (Long lastAlgId : lastAlgIds) {
            Integer result = (Integer) Execute.execute(Execute.UPDATE_TYPE, ALGORITHM_MAPPER_ADDRESS + "deleteAlgorithmByAlgorithmId", new Object[]{lastAlgId});
            if (result == 1) {
                i++;
            }
        }
        if (i == lastAlgIds.size()) {
            return true;
        }
        return false;
    }

    /**
     * 通过算法id查找算法is_default字段
     *
     * @param algId 算法id
     * @return Integer
     * @author Ding YuXing
     * @date 2021/8/8 10:01
     */
    private Integer findIsDefaultByAlgId(Long algId) {
        List<Algorithm> algorithms = (List<Algorithm>) Execute.execute(Execute.QUERY_TYPE, ALGORITHM_MAPPER_ADDRESS + "findIsDefaultByAlgId", new Object[]{algId});
        return algorithms.get(0).getIsDefault();
    }


    /**
     * 上传算法文件到本地
     * @param req
     * @return String
     * @author Ding YuXing
     * @date 2021/10/19 22:20
     */
    private String uploadAlg(HttpServletRequest req) {
        //判断上传的文件是普通表单还是带文件的表单
        if (!ServletFileUpload.isMultipartContent(req)) {
            return "";//如果是普通文件，直接返回
        }//通过if就是带文件的表单

        //创建上传文件的保存路径，建议在WEB-INF路径下
        String uploadPath = req.getSession().getServletContext().getRealPath("/WEB-INF/lib");
        System.out.println("上传文件的保存路径:"+uploadPath);
        File uploadFile = new File(uploadPath);
        if (!uploadFile.exists()) {
            uploadFile.mkdirs();//如果保存目录不存在，就创建一个目录
        }

        //创建临时路径
        String tmpPath = req.getServletContext().getRealPath("/WEB-INF/tmp");
        File tmpFile = new File(tmpPath);
        if (!tmpFile.exists()) {
            tmpFile.mkdirs();//如果临时目录不存在，就创建一个目录
        }

        //创建DiskFileItemFactory对象，处理文件上传路径或者大小限制的；
        DiskFileItemFactory factory = new DiskFileItemFactory();
        //设置一个缓冲区，当上传文件大于这个缓冲区的时候，将它放在临时文件中
        factory.setSizeThreshold(1024 * 1024);//缓存区大小为1兆
        factory.setRepository(tmpFile);//临时目录的保存目录

        //获取ServletFileUpload
        ServletFileUpload upload = new ServletFileUpload(factory);
        //监听文件上传进度
        upload.setProgressListener(new ProgressListener() {
            @Override
            //l:已经读取到文件的大小
            //l1:文件大小
            public void update(long l, long l1, int i) {
                System.out.println("总大小:" + l1 + "\t" + "已上传:" + l);
            }
        });
        upload.setHeaderEncoding("utf-8");//处理乱码问题
        upload.setFileSizeMax(1024 * 1024 * 10);//设置单个文件的最大值
        upload.setSizeMax(1024 * 1024 * 10);//设置总共能够上传文件的大小

        //处理上传的文件
        String msg = "";
        //上传文件名
        String fileName = "";
        String lastFile = "";
        try {
            //把前端请求解析，封装成一个FileItem对象
            List<FileItem> fileItems = upload.parseRequest(req);
            for (FileItem fileItem : fileItems) {
                if (fileItem.isFormField()) {//判断上传的文件是普通的表单还是带文件的表单
                    //getFieldName指的是前端表单控件的name；
                    String name = fileItem.getFieldName();
                    String value = fileItem.getString("utf-8");//处理 乱码
                    System.out.println(name + ":" + value);
                } else {//判断它是上传文件
                    //========================处理文件=============================//

                    //拿到文件名字
                    String uploadFileName = fileItem.getName();
                    System.out.println("上传的文件名：" + uploadFileName);
                    if (uploadFileName.trim().equals("") || uploadFileName == null) {
                        continue;
                    }

                    //获得上传文件名
                    fileName = uploadFileName.substring(uploadFileName.lastIndexOf("/") + 1);
                    //获得文件的后缀名
                    String fileExtName = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1);
                    System.out.println("文件信息 [文件名：" + fileName + "---文件类型" + fileExtName + "]");

                    //使用上传时间,保证文件名唯一
//                String uuidPath = UUID.randomUUID().toString();
                    String uploadTime = new SimpleDateFormat("yyMMddHHmmss").format(Calendar.getInstance().getTime());

                    String fileNameNoExt = fileName.substring(0, fileName.lastIndexOf("."));

                    lastFile = fileNameNoExt + uploadTime + "." + fileExtName;
                    System.out.println("最后的文件名: " + lastFile);
                    //==========================处理文件完毕=========================//

                    //获得文件上传的流
                    InputStream inputStream = fileItem.getInputStream();

                    FileOutputStream fos = new FileOutputStream(uploadPath + "/" + lastFile);

                    //创建一个缓冲区
                    byte[] buffer = new byte[1024 * 1024];

                    //判断是否读取完毕
                    int len = 0;
                    //如果大于0说明还存在数据；
                    while ((len = inputStream.read(buffer)) > 0) {
                        fos.write(buffer, 0, len);
                    }

                    //关闭流
                    fos.close();
                    inputStream.close();

                    msg = "文件上传成功！";
                    System.out.println(msg);
                    fileItem.delete();//上传成功，清除临时文件
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return lastFile;
    }

    /**
     * 添加算法
     *
     * @param algorithm 算法对象
     * @return boolean
     * @author Ding YuXing
     * @date 2021/8/8 10:24
     */
    private boolean addAlgorithm(Algorithm algorithm) {
        //传到这里的算法对象还少了几个参数
        //包含的参数有算法名，算法类型，算法存储位置(文件名)，备注，用户id
        //要增加主键id，is_deleted字段，gmt_create创建时间，gmt_modified修改时间，is_default字段

        Long id = DbUtil.generateId();
        algorithm.setId(id);
        algorithm.setIsDeleted(0);
        algorithm.setIsDefault(0);
        //创建时间
        Timestamp gmtCreateTime = new Timestamp(System.currentTimeMillis());
        algorithm.setGmtCreate(gmtCreateTime);
        algorithm.setGmtModified(gmtCreateTime);

        Integer result = (Integer) Execute.execute(Execute.UPDATE_TYPE, ALGORITHM_MAPPER_ADDRESS + "addAlgorithm", new Object[]{algorithm});
        if (result > 0) {
            return true;
        }
        return false;
    }

    /**
     * 通过id查找算法
     *
     * @param algId
     * @return com.bluedot.www.core.pojo.DO.Algorithm
     * @author GUOZHIPENG
     * @date 2021/9/1 15:36
     */
    private Algorithm findAlgorithmById(Long algId) {
        List<Algorithm> algorithms = (List<Algorithm>) Execute.execute(Execute.QUERY_TYPE, ALGORITHM_MAPPER_ADDRESS + "findAlgorithmById", new Object[]{algId});
        return algorithms.size() == 0 ? null : algorithms.get(0);
    }

    /**
     * 通过id查找算法(视图表中的)
     *
     * @param algId
     * @return com.bluedot.www.core.pojo.VO.ViewAlgorithmInfo
     * @author Ding YuXing
     * @date 2021/10/1 15:36
     */
    private ViewAlgorithmInfo findAlgViewById(Long algId) {
        List<ViewAlgorithmInfo> records = (List<ViewAlgorithmInfo>) Execute.execute(Execute.QUERY_TYPE,
                ALGORITHM_MAPPER_ADDRESS + "findAlgViewById", new Object[]{algId});
        return records.get(0);
    }

    /**
     * 获得算法的总数量
     *
     * @return java.lang.Integer
     * @author He Peng
     * @date 2021/9/8 19:51
     */
    private Integer countAlgorithm() {
        List<Integer> tmp = (List<Integer>) Execute.execute(Execute.QUERY_TYPE, ALGORITHM_MAPPER_ADDRESS + "countAlgorithm", null);
        if (tmp != null && tmp.size() > 0) {
            return tmp.get(0);
        }
        return null;
    }

}
